﻿/* ***** BEGIN LICENSE BLOCK *****
 *
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (the "License"); you may not use this file except in compliance with the
 * License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 * 
 * The Original Code is CnCNet Server code, Released 30th December 2009.
 * 
 * The Initial Developers of the Original Code are
 * Adam Krock and John Sanderson. Portions created by
 * the Initial Developer are Copyright (C) 2009
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s): Adam Krock, John Sanderson.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 * 
 * ***** END LICENSE BLOCK *****/

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Net.Sockets;
using System.Collections.Generic;
using System.Threading;
using System.Runtime.InteropServices;


namespace cncNetServer
{
    public class Program
    {
        // Client thread variables
        public static Thread myClientThread;
        public static ManualResetEvent myListenEvent = new ManualResetEvent(false);
        public static ushort myClientListenPort = 22600;
        public static ushort myClientBufferSize = 8196;
        //public static byte[] myBuffer;

        // Chat thread variables
        public static Thread myChatClientThread;

        // NAT thread variables
        public static Thread myNatListenThread;
        public static ushort myNatListenPort = 8054;
        public static short myNatBufferSize = 1024;

        // List/Database variables
        public static clientList myClients;
          	
        static void Main(string[] args)
        {
            try
            {
                
                //Intialize list/database objects
                myClients = new clientList();
		
				// Initialize Main threads
                // TCP client thread
                myClientThread = new Thread(new ThreadStart(startClientListenThread));
                myClientThread.Start();
            }
            catch (Exception e)
            {
                Console.WriteLine("Main-"+e.Message);
            }
        }

        // TCP client listen thread methods
        public static void startClientListenThread()
        {
            try
            {
                Console.WriteLine("Client listen thread started");
                Socket tempOne = new Socket(AddressFamily.InterNetwork,
                            SocketType.Stream,
                            ProtocolType.Tcp);

                IPEndPoint tempTwo = new IPEndPoint(IPAddress.Any, myClientListenPort);
                tempOne.Bind(tempTwo);
                tempOne.Listen(100);
                while (true)
                {
                    myListenEvent.Reset();
                    tempOne.BeginAccept(new AsyncCallback(accepted), tempOne);
                    myListenEvent.WaitOne();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("startClientListenThread-" + e.Message);
            }
        }
        public static void accepted(IAsyncResult ar)
        {
            try
            {
                myListenEvent.Set();
                Socket tempOne = (Socket)ar.AsyncState;
                client tempTwo = new client(tempOne.EndAccept(ar), myClientBufferSize);
                tempTwo.mySocket.BeginReceive(tempTwo.myBuffer, 0, tempTwo.myBuffer.Length, 0, new AsyncCallback(processStart), tempTwo);
            }
            catch (Exception e)
            {
                Console.WriteLine("accepted-" + e.Message);
            }
        }
        public static void processStart(IAsyncResult ar)
        {
            client tempOne = (client)ar.AsyncState;
            try
            {
                int tempTwo = tempOne.mySocket.EndReceive(ar);
                if (!tempTwo.Equals(0))
                {     
                    // How to use the List buffer;
                    switch (tempOne.myBuffer[0])
                    {
                        // User List request
                        case 56:
                            // Get User list bytes
                            byte[] tempThree = getUserListBytes(tempOne);
                            // Send user list
                            tempOne.mySocket.BeginSend(tempThree, 0, tempThree.Length, 0, new AsyncCallback(processEnd), tempOne);
                            break;
                        // Initial connection
                        case 117:
                            // Get User list bytes
                            byte[] tempFour = getUserListBytes(tempOne);
                            // Get registration packet
                            tempOne.setClientDetails(tempOne.myBuffer);
                            if (!myClients.exists(tempOne))
                            {
                                // Add client to client list
                                myClients.addClient(tempOne);
                                // Send IP address to new client
                                tempOne.mySocket.Send(System.Text.ASCIIEncoding.ASCII.GetBytes(tempOne.myIPString));
                            }
                            // Start process again
                            tempOne.mySocket.BeginSend(tempFour, 0, tempFour.Length, 0, new AsyncCallback(processEnd), tempOne);
                            break;
                    }    
                }
                else
                {
                    Console.WriteLine("[" + myClients.Count + "]Client disconnecting-" + tempOne.myIPString + ":" + tempOne.myPort);
                    myClients.removeClient(tempOne);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("processStart-" + e.Message);
                myClients.removeClient(tempOne);
            }
        }
        public static void processEnd(IAsyncResult ar)
        {
            client tempOne = (client)ar.AsyncState;
            try
            {           
                tempOne.mySocket.EndSend(ar);
                tempOne.mySocket.BeginReceive(tempOne.myBuffer, 0, tempOne.myBuffer.Length, 0, new AsyncCallback(processStart), tempOne);              
            }
            catch (Exception e)
            {
                Console.WriteLine("processEnd-" + e.Message);
                myClients.removeClient(tempOne);
            }
        }
		

        // Helper methods
        public static byte[] getUserListBytes(client c)
        {
            string tempThree = string.Empty;
            // Built list of cncNet Clients
            foreach (client a in myClients)
            {
                // Concat the string with each client that doesnt equal the one requesting the send
                if (!a.myIPString.Equals(c.myIPString))
                {
                    tempThree = tempThree + "@" + a.myName +
                             ":" + a.myIPString + ":" + a.myPort.ToString();
                }
            }
            return System.Text.ASCIIEncoding.ASCII.GetBytes(tempThree);
        }
    }
}
